home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Extra 1997 #1 / Amiga Plus Extra 1997 #1.iso / programme / tools / leoutils / leotune.c < prev    next >
C/C++ Source or Header  |  1996-11-25  |  18KB  |  722 lines

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4.  
  5. #define VERSION "4.14"
  6.  
  7. #if defined _AMIGA || defined AMIGA
  8.     #include <dos/dos.h>
  9.     #if defined __SASC
  10.         #include <proto/dos.h>
  11.     #else
  12.         #include <clib/dos_protos.h>
  13.     #endif
  14. #else
  15.     #include <time.h>
  16. #endif
  17.  
  18. #include "LeoLib.h"
  19.  
  20. #define DIRECTORY    "Fortunes:"
  21. #define FORTUNEBASE  "LeoTunes"
  22. #define INDEX         ".idx"
  23. #define INDEX4        ".idx4"
  24.  
  25. #define    TRUE    1
  26. #define FALSE    0
  27.  
  28. #define WW_DEFWIDTH    78
  29.  
  30.  
  31.  
  32. /* The following definitions are for ANSI C compilers with bad stdio.h */
  33. #if !defined SEEK_SET
  34.     #define SEEK_SET 0
  35. #endif
  36.  
  37. #if !defined _IOFBF
  38.     #define _IOFBF 0
  39. #endif
  40.  
  41.  
  42.  
  43. /* AMIGA VERSION STRING */
  44.  
  45. #if defined _AMIGA || defined AMIGA
  46.     static char *Version = "$VER:LeoTune " VERSION
  47.     #if defined __SASC
  48.         " " __AMIGADATE__ " ©1992-96 Leopold-Soft, leopold@cs.tut.fi"
  49.     #endif
  50.     ;
  51. #endif
  52.  
  53.  
  54. /* MY OWN TYPE DEFINITIONS */
  55.  
  56. #if !defined _AMIGA && !defined AMIGA
  57.     typedef short BOOL;
  58. #endif
  59. typedef unsigned char uchar;
  60.  
  61.  
  62. /* PROTOTYPES */
  63.  
  64. static void CreateIndexFile(void);
  65. static void ShowFortune(int Number);
  66. static int ShowFortuneFromMem(FILE* OutFile, uchar *Text, long Length);
  67. static uchar ToIso11(uchar c);
  68. static void GrepFortunes(uchar *GrepStr, BOOL CaseSensitive);
  69. #if defined _AMIGA || defined AMIGA
  70.     static ULONG GetAmigaTicks(void);
  71. #endif
  72. static void Bufferize(FILE *File);    /* Only one file may be bufferized at a time */
  73.  
  74. static void PutInit(FILE *OutFile);
  75. static void PutWord(uchar *Str);
  76. static void PutEOL(void);
  77. static int PutEnd(void);            /* returns lines handled */
  78. static void PutTab(void);
  79. static void PutSpace(void);
  80. static void PutSetWidth(int Width);
  81. static void PutPurgeSpace(void);
  82. static void PutSetIndent(BOOL On);
  83. static void PutDoIndent(void);
  84.  
  85. /* VARIABLES */
  86.  
  87. static char    FFile[1024], FIndex[1024], FIndex4[1024];
  88. static int    CurrArg = 0, WordWrapWidth = WW_DEFWIDTH;
  89. static BOOL    WantedIndexRecount = FALSE;
  90. static BOOL    LongOutputMode = TRUE;
  91. static BOOL    SignatureMode = FALSE;
  92. static BOOL    Iso11 = FALSE, Grep = FALSE, GrepCaseSensitivity;
  93. static int WantedFortune = 0, GrepArg;
  94. static char *Directory;
  95. static void *InBuff = NULL;
  96.  
  97. static unsigned long FEnd0 =
  98.         (('\n' << 24) | ('%' << 16) | ('%' << 8) | '\n');
  99. static unsigned long FEnd1 =
  100.         (('\n' << 24) | ('#' << 16) | ('#' << 8) | '\n');
  101.  
  102.  
  103.  
  104. /* MAIN PROGRAM */
  105.  
  106. int main(int argc, char **argv) {
  107.     static char *ColumnsStr;
  108.     static int Columns = 0;
  109.  
  110.     if ((ColumnsStr = getenv("COLUMNS")) && (Columns = atoi(ColumnsStr)))
  111.         WordWrapWidth = Columns;
  112.  
  113.     FFile[0] = '\000';
  114.     if (!(Directory = getenv("LEOTUNEDIR"))) {
  115.         if (Directory = getenv("HOME")) {
  116.             strcpy(FFile, Directory);
  117.             if (FFile[strlen(FFile)-1] != '/'
  118. #if defined _AMIGA || defined AMIGA
  119.                     && FFile[strlen(FFile)-1] != ':'
  120. #endif
  121.                                     )
  122.                 strcat(FFile, "/");
  123.             strcat(FFile, "text/");
  124.         } else {
  125.             strcpy(FFile, DIRECTORY);
  126.         }
  127.     } else {
  128.         strcpy(FFile, Directory);
  129.         if (FFile[strlen(FFile)-1] != '/'
  130. #if defined _AMIGA || defined AMIGA
  131.                 && FFile[strlen(FFile)-1] != ':'
  132. #endif
  133.                                 )
  134.             strcat(FFile, "/");
  135.     }
  136.     strcat(FFile, FORTUNEBASE);
  137.     strcpy(FIndex, FFile);
  138.     strcat(FIndex, INDEX);
  139.     strcpy(FIndex4, FFile);
  140.     strcat(FIndex4, INDEX4);
  141.  
  142.     while(++CurrArg < argc) {
  143.         if (!strcmp(argv[CurrArg],"-i") || !strcmp(argv[CurrArg],"index")) {
  144.             WantedIndexRecount = TRUE;
  145.         } else if (!strcmp(argv[CurrArg],"-s") || !strcmp(argv[CurrArg],"short")) {
  146.             LongOutputMode = FALSE;
  147.         } else if (!strcmp(argv[CurrArg],"-4") || !strcmp(argv[CurrArg],"sig")) {
  148.             SignatureMode = TRUE;
  149.         } else if (!strcmp(argv[CurrArg],"-7") || !strcmp(argv[CurrArg],"iso11")) {
  150.             Iso11 = TRUE;
  151.         } else if ((!strcmp(argv[CurrArg],"-t") || !strcmp(argv[CurrArg],"tune")) && argc > CurrArg) {
  152.                 WantedFortune = atoi(argv[++CurrArg]);
  153.                 if (WantedFortune < 1) {
  154.                     fprintf(stderr, "\nWarning: %d is not a valid fortune number, ignoring...\n",
  155.                             WantedFortune);
  156.                     WantedFortune = 0;
  157.                 }
  158.         } else if ((!strcmp(argv[CurrArg],"-g") || !strcmp(argv[CurrArg],"grep")) && argc > CurrArg) {
  159.             GrepArg = ++CurrArg;
  160.             Grep = TRUE;
  161.             GrepCaseSensitivity = FALSE;
  162.         } else if ((!strcmp(argv[CurrArg],"-G") || !strcmp(argv[CurrArg],"GREP")) && argc > CurrArg) {
  163.             GrepArg = ++CurrArg;
  164.             Grep = TRUE;
  165.             GrepCaseSensitivity = TRUE;
  166.         } else if ((!strcmp(argv[CurrArg],"-w") || !strcmp(argv[CurrArg],"width")) && argc > CurrArg) {
  167.                 WordWrapWidth = atoi(argv[++CurrArg]);
  168.         } else if (!strcmp(argv[CurrArg],"-h") || !strcmp(argv[CurrArg],"?")) {
  169.             printf("\nLeofortune " VERSION " by Henrik Herranen " __DATE__ "\n\n"
  170.                 "Usage: %s [?] | [-h] | [-i] | [-s] | [-4] | [filename]\n\n"
  171.                 "-h or ?:     Show this help page.\n"
  172.                 "-i or index: Recreate the fortune index file. This option must\n"
  173.                 "             be used every time the fortune database is updated.\n"
  174.                 "-s or short: Don't insert any empty lines.\n"
  175.                 "-4 or sig  : Use an index file with only the fortunes with\n"
  176.                 "             mostly 4 lines of less than 80 characters each.\n"
  177.                 "-7 or iso11: Convert some 8-bit ANSI characters to their ISO 11\n"
  178.                 "             (Swedish translation table) equivalents. Replace\n"
  179.                 "             rest of the 8-bit characters with '?'\n"
  180.                 "-t x or      Show a specific fortune from the database. The\n"
  181.                 "     tune x: first fortune is numbered 1.\n"
  182.                 "-g x or      Displays all fortunes that have the string x in\n"
  183.                 "     grep x: themselves (case-insensitive).\n"
  184.                 "-G x or      Displays all fortunes that have the string x in\n"
  185.                 "     GREP x: themselves (case-sensitive).\n"
  186.                 "-w x or      Displays all word-wrapped in x columns. The default\n"
  187.                 "    width x: is 79 or whatever is in $COLUMNS\n"
  188.                 "filename:    the named file will be used as the fortune file.\n\n"
  189.                 ,argv[0]);
  190.                 exit(EXIT_SUCCESS);
  191.         } else {
  192.             strcpy(FFile,argv[CurrArg]);
  193.             strcpy(FIndex,argv[CurrArg]);
  194.             strcat(FIndex,INDEX);
  195.             strcpy(FIndex4,argv[CurrArg]);
  196.             strcat(FIndex4,INDEX4);
  197.         }
  198.     }
  199.  
  200.     if (WantedIndexRecount) CreateIndexFile();
  201.     else if (Grep) GrepFortunes((uchar *) argv[GrepArg], GrepCaseSensitivity);
  202.     else ShowFortune(WantedFortune);
  203.  
  204.     return EXIT_SUCCESS;
  205. }
  206.  
  207.  
  208.  
  209. /* CREATES INDEX FILES */
  210.  
  211. static void CreateIndexFile(void) {
  212.     static FILE    *InFile,*InFile2,*OutFile,*OutFile4;
  213.     static long    FortuneCount=0,FortuneCount4=0;
  214.     static int    InC;
  215.     static long    FortuneBeginAddress=0;
  216.     static long    Width=0,Widest=0,Lines=0;
  217.     static int    i;
  218.     static long OldFortuneAmount=0,OldFortuneAmount4=0;
  219.     static long WordWrappedFortunes, WordWrappedFortunes4;
  220.     static unsigned long Last4 = 0;
  221.     static long FortuneLength = 0, Loc = 0;
  222.     static long MaxFortuneLength = 0;
  223.     static uchar *FortuneText = NULL;
  224.  
  225.     SignatureMode = TRUE;
  226.  
  227.     if (!(InFile = fopen(FFile,"rb"))) {
  228.         fprintf(stderr,"Couldn't open fortune file %s for reading!\n",FFile);
  229.         exit(EXIT_FAILURE);
  230.     }
  231.     Bufferize(InFile);
  232.  
  233.     if (InFile2 = fopen(FIndex,"rb")) {
  234.         fread(&OldFortuneAmount,1,sizeof(OldFortuneAmount),InFile2);
  235.         fclose(InFile2);
  236.     }
  237.  
  238.     if (InFile2 = fopen(FIndex4,"rb")) {
  239.         fread(&OldFortuneAmount4,1,sizeof(OldFortuneAmount4),InFile2);
  240.         fclose(InFile2);
  241.     }
  242.  
  243.     if (!(OutFile = fopen(FIndex,"wb"))) {
  244.         fprintf(stderr,"Couldn't open fortune file %s for writing!\n",FIndex);
  245.         exit(EXIT_FAILURE);
  246.     }
  247.  
  248.     if (!(OutFile4 = fopen(FIndex4,"wb"))) {
  249.         fprintf(stderr,"Couldn't open fortune file %s for writing!\n",FIndex4);
  250.         exit(EXIT_FAILURE);
  251.     }
  252.  
  253.     /* Leave some space for the # of fortunes */
  254.     for (i=0; i<sizeof(FortuneCount); i++) {
  255.         fputc('?',OutFile);
  256.         fputc('?',OutFile4);
  257.     }
  258.  
  259.     do {
  260.         InC = fgetc(InFile);
  261.         Loc++;
  262.         FortuneLength++;
  263.         if (FortuneLength > MaxFortuneLength) {
  264.             static uchar *Tmp;
  265.             long OldFLen = MaxFortuneLength;
  266.             MaxFortuneLength = MaxFortuneLength * 120 / 100 + 100;
  267.             if (!(Tmp = malloc((size_t) MaxFortuneLength))) {
  268.                 fprintf(stderr, "Couldn't allocate %d bytes!\n", FortuneLength);
  269.                 exit(EXIT_FAILURE);
  270.             }
  271.             if (OldFLen) {
  272.                 memcpy(Tmp, FortuneText, (size_t) OldFLen);
  273.                 free (FortuneText);
  274.             }
  275.             FortuneText = Tmp;
  276.         }
  277.         FortuneText[FortuneLength-1] = InC;
  278.         Last4 = (Last4 << 8) | InC;
  279.         if (Last4 == FEnd0 || Last4 == FEnd1) {
  280.             FortuneLength -= 3;
  281.             FortuneCount++;
  282.             fwrite(&FortuneBeginAddress, 1, sizeof(FortuneBeginAddress), OutFile);
  283.             fwrite(&FortuneLength, 1, sizeof(FortuneLength), OutFile);
  284.             if (Last4 == FEnd1) WordWrappedFortunes++;
  285.  
  286.             if (Last4 == FEnd0 && Widest < 80 && Lines < 5 ||
  287.                 Last4 == FEnd1 && ShowFortuneFromMem(NULL, FortuneText, FortuneLength) < 5) {
  288.                 FortuneCount4++;
  289.                 fwrite(&FortuneBeginAddress, 1, sizeof(FortuneBeginAddress), OutFile4);
  290.                 fwrite(&FortuneLength, 1, sizeof(FortuneLength), OutFile4);
  291.                 if (Last4 == FEnd1) WordWrappedFortunes4++;
  292.             }
  293.             Width = 0; Widest = 0; Lines = 0;
  294.             FortuneBeginAddress = Loc;
  295.             FortuneLength = 0;
  296.         } else {
  297.             if (InC == '\n') {
  298.                 if (Width > Widest) Widest = Width;
  299.                 Width = 0;
  300.                 Lines++;
  301.             } else if (InC == '\t') {
  302.                 Width = (Width + 8) & ~7L;
  303.             } else {
  304.                 Width++;
  305.             }
  306.         }
  307.     } while (InC != -1);
  308.  
  309.     fseek(OutFile, 0, SEEK_SET);
  310.     fwrite(&FortuneCount, 1, sizeof(FortuneCount), OutFile);
  311.     fseek(OutFile4, 0, SEEK_SET);
  312.     fwrite(&FortuneCount4, 1, sizeof(FortuneCount4), OutFile4);
  313.  
  314.     fclose(OutFile);
  315.     fclose(InFile);
  316.  
  317.     printf("\n%d entries (%d+%d; %d new) created in %s\n"
  318.              "%d entries (%d+%d; %d new) created in %s\n\n",
  319.             FortuneCount,
  320.                 WordWrappedFortunes, FortuneCount-WordWrappedFortunes,
  321.                 FortuneCount-OldFortuneAmount, FIndex,
  322.             FortuneCount4,
  323.                 WordWrappedFortunes4, FortuneCount4-WordWrappedFortunes4,
  324.                 FortuneCount4-OldFortuneAmount4, FIndex4);
  325.  
  326.     return;
  327. }
  328.  
  329.  
  330.  
  331.  
  332. /* DISPLAYS A FORTUNE */
  333.  
  334. static void ShowFortune(int FNumber) {
  335.     static long Random, i;
  336.     static FILE *InFile;
  337.     static long FortuneBeginAddress,FortuneLength,FortuneAmount;
  338.     static uchar *FortuneText;
  339.  
  340.     if (SignatureMode) strcpy(FIndex, FIndex4);
  341.     if (!(InFile = fopen(FIndex,"rb"))) {
  342.         fprintf(stderr,"Couldn't open fortune file %s for reading!\n",FIndex);
  343.         exit(EXIT_FAILURE);
  344.     }
  345.  
  346.     fread(&FortuneAmount,1,sizeof(FortuneAmount),InFile);
  347.     if (FNumber > 0) {
  348.         if (FNumber <= FortuneAmount) {
  349.             Random = FNumber - 1;
  350.         } else {
  351.             fprintf(stderr,"\nWarning: %d is not a valid fortune number (max=%d), ignoring...\n",
  352.                 FNumber, FortuneAmount);
  353.             FNumber = 0;
  354.         }
  355.     }
  356.  
  357.     if (!FNumber) {
  358. #if defined _AMIGA || defined AMIGA
  359.         if (!(Random = (GetAmigaTicks() >> 1))) {
  360. #else
  361.         if ((Random = time(NULL)) < 0) {
  362.             Random = 0;
  363. #endif
  364.             fprintf(stderr, "System clock not set, resetting to fortune #0!\n");
  365.         } else {
  366.             Random %= FortuneAmount;
  367.         }
  368.     }
  369.  
  370.     fseek(InFile, Random*(sizeof(FortuneBeginAddress)+sizeof(FortuneLength))+sizeof(FortuneAmount), SEEK_SET);
  371.  
  372.     fread(&FortuneBeginAddress,1,sizeof(FortuneBeginAddress),InFile);
  373.     fread(&FortuneLength,1,sizeof(FortuneLength),InFile);
  374.  
  375.     fclose(InFile);
  376.  
  377.     if (!(InFile = fopen(FFile,"rb"))) {
  378.         fprintf(stderr, "Couldn't open fortune file %s for reading!\n",FFile);
  379.         exit(EXIT_FAILURE);
  380.     }
  381.  
  382.     if(LongOutputMode) fputc('\n', stdout);
  383.     fseek(InFile, FortuneBeginAddress, SEEK_SET);
  384.     if (!(FortuneText = malloc((size_t) (FortuneLength+1)))) {
  385.         fprintf(stderr, "Couldn't allocate %d bytes!\n", FortuneLength);
  386.         exit(EXIT_FAILURE);
  387.     }
  388.  
  389.     fread(FortuneText, 1, (size_t) (FortuneLength+1), InFile);
  390.     if (Iso11) {
  391.         for (i=0; i<FortuneLength; i++) {
  392.             FortuneText[i] = ToIso11(FortuneText[i]);
  393.         }
  394.     }
  395.  
  396.     if (FortuneText[FortuneLength] == '%') {        /* No WordWrap required */
  397.         fwrite(FortuneText, 1, (size_t) FortuneLength, stdout);
  398.     } else {                                        /* WordWrap required    */
  399.         ShowFortuneFromMem(stdout, FortuneText, FortuneLength);
  400.     }                                                /* End of if (WordWrap)    */
  401.  
  402.     free(FortuneText);
  403.  
  404.     if(LongOutputMode) fputc('\n', stdout);
  405.  
  406.     fclose(InFile);
  407.  
  408.     return;
  409. }
  410.  
  411.  
  412.  
  413. /* RETURNS THE ISO 11 EQUIVALENT OF AN 8-BIT UNSIGNED CHARACTER */
  414.  
  415. static uchar ToIso11(uchar c) {
  416.     switch (c) {
  417.         case 0304 : c = '[' ; break;    /* A" */
  418.         case 0344 : c = '{' ; break;    /* a" */
  419.         case 0326 : c = '|' ; break;    /* O", intentionally _NOT_ '\\' */
  420.         case 0366 : c = '|' ; break;    /* o" */
  421.         case 0305 : c = ']' ; break;    /* A' */
  422.         case 0345 : c = '}' ; break;    /* a' */
  423.         case 0334 : c = '^' ; break;    /* U" */
  424.         case 0374 : c = '~' ; break;    /* u" */
  425.         case 0311 : c = '@' ; break;    /* E' */
  426.         case 0351 : c = '`' ; break;    /* e' */
  427.         case 0253 : c = '"' ; break;    /* << */
  428.         case 0273 : c = '"' ; break;    /* >> */
  429.         default :   if (c > 126) c = '?'; break;
  430.     }
  431.     return c;
  432. }
  433.  
  434.  
  435.  
  436.  
  437. /* SHOWS FORTUNES THAT HAVE THE SPECIFIED STRING IN THEM */
  438.  
  439. static void GrepFortunes(uchar *FromStr, BOOL Sensitive) {
  440.     static uchar GrepStr[80];
  441.     static uchar Last[80];
  442.     int Curr = 1, LastLoc = 0;
  443.     BOOL OldSigMode = SignatureMode,
  444.         OldLOMode = LongOutputMode,
  445.         SkipUntilEndOfFortune = FALSE;
  446.     static FILE *InFile;
  447.     static int InC, GrepStrLen, i;
  448.     unsigned long Last4 = 0, Magic = 0, MagicGrepStr = 0;
  449.     static char Line[71] =
  450.         "----------------------------------------------------------------------";
  451.     SignatureMode = FALSE;
  452.     LongOutputMode = FALSE;
  453.  
  454.     GrepStr[0] = '\000';
  455.     for (i = 0; i < 80 && FromStr[i]; i++) {
  456.         if (Sensitive) GrepStr[i] = FromStr[i];
  457.         else GrepStr[i] = ToUpper(FromStr[i]);
  458.         MagicGrepStr += GrepStr[i];
  459.         Last[i] = 0;
  460.     }
  461.     GrepStr[79] = '\000';
  462.     GrepStrLen = strlen((char *) GrepStr);
  463.     if (!GrepStrLen) return;
  464.  
  465.     if (!(InFile = fopen(FFile,"rb"))) {
  466.         fprintf(stderr,"Couldn't open fortune file %s for reading!\n",FFile);
  467.         exit(EXIT_FAILURE);
  468.     }
  469.     Bufferize(InFile);
  470.  
  471.     i = 20 + strlen((char *) GrepStr);
  472.     if (i > 70) i = 70;
  473.     printf("---------- Grep: \"%s\" %s\n", GrepStr, &Line[i]);
  474.  
  475.     while ((InC = fgetc(InFile)) != -1) {
  476.         Last4 = (Last4 << 8) | InC;
  477.         if (!Sensitive) InC = ToUpper(InC);
  478.         if (InC == '\n') InC = ' ';
  479.  
  480.         LastLoc++;
  481.         if (LastLoc >= GrepStrLen) LastLoc = 0;
  482.         Magic -= Last[LastLoc];
  483.         Magic += InC;
  484.         Last[LastLoc] = InC;
  485.  
  486.         if (Last4 == FEnd0 || Last4 == FEnd1) {
  487.             Curr++;
  488.             SkipUntilEndOfFortune = FALSE;
  489.         }
  490.  
  491.         if (Magic == MagicGrepStr && !SkipUntilEndOfFortune) {
  492.             int k = LastLoc, ok = TRUE;
  493.             for (i=GrepStrLen-1; i>=0; i--) {
  494.                 if (Last[k] != GrepStr[i]) ok = FALSE;
  495.                 k--;
  496.                 if (k<0) k = GrepStrLen-1;
  497.             }
  498.             if (ok) {
  499.                 ShowFortune(Curr);
  500.                 printf("%s\n", Line);
  501.                 SkipUntilEndOfFortune = TRUE;
  502.             }
  503.         }
  504.     }
  505.  
  506.     fclose(InFile);
  507.     SignatureMode = OldSigMode;
  508.     LongOutputMode = OldLOMode;
  509. }
  510.  
  511.  
  512.  
  513.  
  514. /* RETURNS THE CURRENT TIME IN FIFTIETH OF SECONDS, AMIGA SPECIFIC */
  515.  
  516. #if defined _AMIGA || defined AMIGA
  517. static ULONG GetAmigaTicks(void) {
  518.     static struct DateStamp ds;
  519.     DateStamp(&ds);
  520.     return (ULONG) ((ULONG) ds.ds_Tick +
  521.             ((ULONG) ds.ds_Minute +
  522.             (ULONG) ds.ds_Days*24*60)*60*TICKS_PER_SECOND);
  523. }
  524. #endif
  525.  
  526.  
  527.  
  528.  
  529. /***********************************\
  530. *                                    *
  531. *    Text displaying put-routines    *
  532. *                                    *
  533. \***********************************/
  534.  
  535. static FILE *PutFile=NULL;
  536. static int PutPos, PutMaxColumn = 79, PutLines, PutIndent;
  537. static BOOL PutLastSpace;
  538.  
  539. static void PutInit(FILE *OutFile) {
  540.     PutFile = OutFile;
  541.     PutPos = 0;
  542.     PutLastSpace = FALSE;
  543.     PutLines = 0;
  544.     PutIndent = 0;
  545. }
  546.  
  547. static void PutWord(uchar *Str) {
  548.     static int Len;
  549.  
  550.     Len = strlen((char *) Str);
  551.     if (PutPos + Len >= PutMaxColumn) {
  552.         if (PutFile) fputc('\n', PutFile);
  553.         PutPos = 0;
  554.         PutLines++;
  555.         PutDoIndent();
  556.         if (PutFile) fprintf(PutFile, "%s", Str);
  557.         PutPos += Len - 1;
  558.     } else {
  559.         if (PutLastSpace) {
  560.             if (PutFile) fputc(' ', PutFile);
  561.             PutPos++;
  562.         }
  563.         if (PutFile) fprintf(PutFile, "%s", Str);
  564.         PutPos += Len;
  565.     }
  566.     PutLastSpace = FALSE;
  567. }
  568.  
  569. static void PutEOL(void) {
  570.     if (PutFile) fputc('\n', PutFile);
  571.     PutPos = 0;
  572.     PutLines++;
  573.     PutLastSpace = FALSE;
  574.     PutSetIndent(FALSE);
  575. }
  576.  
  577. static void PutTab(void) {
  578.     PutPurgeSpace();
  579.     if (PutFile) fputc('\t', PutFile);
  580.     PutPos = (PutPos + 8) & (~7);
  581. }
  582.  
  583. static void PutSpace(void) {
  584.     if (PutLastSpace) {
  585.         PutLastSpace = FALSE;
  586.         PutWord(" ");
  587.     }
  588.     PutLastSpace = TRUE;
  589. }
  590.  
  591. static void PutPurgeSpace(void) {
  592.     if (PutLastSpace) {
  593.         if (PutPos + 1 >= PutMaxColumn) {
  594.             if (PutFile) fputc('\n', PutFile);
  595.             PutPos = 0;
  596.             PutLines++;
  597.             PutDoIndent();
  598.         } else {
  599.             if (PutFile) fputc(' ', PutFile);
  600.             PutPos++;
  601.         }
  602.     }
  603.     PutLastSpace = FALSE;
  604. }
  605.  
  606. static void PutSetWidth(int Width) {
  607.     if (Width < 20) Width = 20;
  608.  
  609.     PutMaxColumn = Width - 1;
  610. }
  611.  
  612. static int PutEnd(void) {
  613.     return PutLines;
  614. }
  615.  
  616. static void PutSetIndent(BOOL On) {
  617.     PutPurgeSpace();
  618.     if (On) PutIndent = PutPos;
  619.     else PutIndent = 0;
  620. }
  621.  
  622. static void PutDoIndent(void) {
  623.     if (PutIndent <= PutMaxColumn-20) {
  624.         while (PutPos < PutIndent) {
  625.             if (PutFile) fputc(' ', PutFile);
  626.             PutPos++;
  627.         }
  628.     }
  629. }
  630.  
  631.  
  632.  
  633.  
  634. /* SHOWS A FORTUNE FILE FROM MEMORY */
  635.  
  636. static int ShowFortuneFromMem(FILE* OutFile, uchar *FortuneText, long FortuneLength) {
  637.     static uchar Word[80];
  638.     int WordLen = 0;
  639.     BOOL LastNL = TRUE, LastEscape = FALSE;
  640.     static int InC;
  641.     static long i;
  642.  
  643.     if (SignatureMode) {
  644.         PutSetWidth(WW_DEFWIDTH);
  645.     } else {
  646.         PutSetWidth(WordWrapWidth);
  647.     }
  648.  
  649.     PutInit(OutFile);
  650.  
  651.     for (i=0; i<FortuneLength; i++) {
  652.         InC = FortuneText[i];
  653.         if (InC == '\\' && i<FortuneLength-1 && !LastEscape) {
  654.             switch (FortuneText[i+1]) {
  655.                 case '<':
  656.                     PutSetIndent(FALSE);
  657.                     i++;
  658.                     break;
  659.                 case '>':
  660.                     if (WordLen) {
  661.                         PutWord(Word);
  662.                         WordLen = 0;
  663.                     }
  664.                     PutSetIndent(TRUE);
  665.                     i++;
  666.                     break;
  667.                 default:
  668.                     LastEscape = 2;
  669.                     break;
  670.             }
  671.         } else if (InC == '\n') {
  672.             if (LastNL) {
  673.                 if (WordLen) {
  674.                     PutWord(Word);
  675.                     WordLen = 0;
  676.                 }
  677.                 PutEOL();
  678.             } else {
  679.                 if (WordLen) {
  680.                     PutWord(Word);
  681.                     WordLen = 0;
  682.                 }
  683.                 PutSpace();
  684.             }
  685.         } else if (InC == '\t') {
  686.             if (WordLen) {
  687.                 PutWord(Word);
  688.                 WordLen = 0;
  689.             }
  690.             PutTab();
  691.         } else {
  692.             if (InC == ' ') {
  693.                 if (WordLen) {
  694.                     PutWord(Word);
  695.                     WordLen = 0;
  696.                 }
  697.                 PutSpace();
  698.             } else {
  699.                 if (WordLen < 78) {
  700.                     Word[WordLen] = InC;
  701.                     Word[++WordLen] = '\000';
  702.                 }
  703.             }
  704.         }
  705.         LastNL = (InC == '\n');
  706.         if (LastEscape) LastEscape--;
  707.     }
  708.     PutEOL();
  709.     return PutEnd();
  710. }
  711.  
  712.  
  713.  
  714. /* CONNECT A FILE TO A 16 KB IO-BUFFER TO ENHANCE FILE HANDLING.    */
  715. /* NOTE: ONLY ONE FILE AT A TIME MAY HAVE THIS BUFFER!                */
  716. /*         THIS FUNCTION MUST BE CALLED IMMEDIATELY AFTER FOPEN()!    */
  717.  
  718. static void Bufferize(FILE *File) {
  719.     if (!InBuff) InBuff = malloc(16384);
  720.     if (InBuff) setvbuf(File, InBuff, _IOFBF, (size_t) 16384);
  721. }
  722.